import sys
from PySide6.QtWidgets import (QApplication, QWidget, QTableWidget, 
                             QTableWidgetItem, QVBoxLayout, QLabel,QHBoxLayout,QMessageBox,QPushButton)
from PySide6.QtCore import Qt,QMimeData, QByteArray, QDataStream,QPoint,Slot,Signal
from PySide6.QtGui import QDrag, QColor, QPixmap, QPainter
import sqlite3
import inc_db as db
from loger import log
import time
from pprint import pprint
class basetable(QTableWidget):
    def __init__(self, rows=5, cols=3, parent=None):
        super().__init__(rows, cols, parent)
        self.setSelectionMode(QTableWidget.ExtendedSelection)
        self.setDragDropMode(QTableWidget.DragDrop)
        self.setAcceptDrops(True)
        self.setDragEnabled(True)
        self.setDefaultDropAction(Qt.CopyAction)

        # 状态变量
        self.last_highlighted = None  # 记录上一个高亮的单元格
        self.forbidden_cells =set()# {(0, 0),(0, 1)}  # 禁止拖放的单元格(行,列)
        self.number_columns = set()  # 只接受数字的列(索引)
        self.cleartable()
        # self.refreshBackground()
        for i in range(self.columnCount()):
            self.setColumnWidth(i,60) #设置列宽

    # def refreshBackground(self):
    #     for row in range(self.rowCount()):
    #         for col in range(self.columnCount()):
    #             item=self.item(row,col)
    #             if (row, col) in self.forbidden_cells:
    #                 item.setBackground(QColor(255, 220, 220))
    #                 item.setToolTip("禁止拖放")
    #             else:
    #                 item.setBackground(QColor(255, 255, 255))
    def cleartable(self):
        for row in range(self.rowCount()):
            for col in range(self.columnCount()):
                item = QTableWidgetItem(f"")
                item.setTextAlignment(Qt.AlignCenter)
                self.setItem(row, col, item)
        return True
    
    def startDrag(self, supported_actions):
        """重写开始拖动方法，自定义拖动数据和视觉反馈"""
        log.debug("startDrag")
        selected_items = self.selectedItems()
        if not selected_items:
            return super().startDrag(supported_actions)
            # 执行拖动并处理结果

        drag=self.creatDrag(selected_items[0])
        if drag is False :
            return False
        drag.exec_(supported_actions)

    def creatDrag(self, item):
        selected_items = self.selectedItems()
        # 过滤掉禁止拖动的单元格
        if (item.row(), item.column())  in self.forbidden_cells :
            return False
        
        # 创建MIME数据
        mime_data = QMimeData()
        
        # 1. 添加Qt默认表格格式(便于表格间拖放)
        # 1. 生成application/x-qabstractitemmodeldatalist格式数据
        byte_array = QByteArray()
        data_stream = QDataStream(byte_array, QDataStream.WriteOnly)

        item=selected_items[0]
        data_stream.writeInt32(item.row())
        data_stream.writeInt32(item.column())
        
        # 写入角色-值对数量（这里包含3种角色）
        data_stream.writeInt32(4)
        
        # 角色1：显示文本
        data_stream.writeInt32(Qt.DisplayRole)
        data_stream.writeQVariant(item.text())
        
        # 角色2：背景色
        data_stream.writeInt32(Qt.BackgroundRole)
        data_stream.writeQVariant(item.background())
        
        # 角色3：提示文本
        data_stream.writeInt32(Qt.ToolTipRole)
        data_stream.writeQVariant(item.toolTip())
        #角色4：对齐方式
        data_stream.writeInt32(Qt.TextAlignmentRole)
        data_stream.writeQVariant(item.textAlignment())
        
        mime_data.setData("application/x-qabstractitemmodeldatalist", byte_array)
        
        # 2. 添加文本格式(兼容其他控件)
        text_data = f"{item.row()},{item.column()},{item.text()}"
        mime_data.setText(text_data)
        
        # 创建拖动对象
        drag = QDrag(self)
        drag.setMimeData(mime_data)
        
        # 设置拖动图标
        drag.setPixmap(self._create_drag_pixmap(item.text()))
        drag.setHotSpot(QPoint(-10, 0))

        return drag
        


    def _create_drag_pixmap(self, dataText):
        """创建自定义拖动图标"""
        pixmap = QPixmap(80, 25)
        pixmap.fill(QColor(255, 255, 255,200))
        
        painter = QPainter(pixmap)
        painter.setPen(QColor(0, 0, 0))
        painter.drawText(pixmap.rect(), Qt.AlignCenter, f"{dataText}")
        painter.drawRect(0, 0, pixmap.width()-1, pixmap.height()-1)
        painter.end()
        
        return pixmap
    
    def dragEnterEvent(self, event):
        """拖入时验证数据"""
        if (
            event.mimeData().hasText() or 
            event.mimeData().hasFormat("application/x-qabstractitemmodeldatalist")
        ):
            event.acceptProposedAction()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        """拖动过程中提供视觉反馈和位置验证"""
        # 清除上一次高亮
        self._clear_highlight()
        
        # 获取目标位置
        pos = event.pos()
        index = self.indexAt(pos)
        
        if not index.isValid():
            event.ignore()
            return
            
        row, col = index.row(), index.column()
        
        # 检查是否为禁止区域
        if (row, col) in self.forbidden_cells:
            event.ignore()
            return
            
        # 高亮目标单元格
        self._highlight_cell(row, col)
        
        event.acceptProposedAction()

    def dragLeaveEvent(self, event):
        """拖动离开时清除高亮"""
        self._clear_highlight()
        super().dragLeaveEvent(event)

    def dropEvent(self, event):
        """处理最终放下的数据"""
        # 清除高亮
        self._clear_highlight()
        
        # 获取目标位置
        pos = event.pos()
        index = self.indexAt(pos)
        target_row, target_col = 0, 0
        
        if index.isValid():
            target_row, target_col = index.row(), index.column()
        else:
            # 拖到空白处，放到最后一行
            target_row = self.rowCount()
            target_col = 0
            self.insertRow(target_row)
        
        # 解析并处理数据
        mime_data = event.mimeData()
        if mime_data.hasFormat("application/x-qabstractitemmodeldatalist"):
            self._process_model_data(mime_data, target_row, target_col)
        elif mime_data.hasText():
            self._process_text_data(mime_data.text(), target_row, target_col)
            
        event.acceptProposedAction()
# ------------------------------
    # 辅助方法
    # ------------------------------
    def _highlight_cell(self, row, col):
        """高亮目标单元格"""
        item = self.item(row, col) or QTableWidgetItem()
        self.last_highlighted = {
            "row": row,
            "col": col,
            "bg": item.background()
        }
        item.setBackground(QColor(255, 245, 157))
        if not self.item(row, col):
            self.setItem(row, col, item)

    def _clear_highlight(self):
        """清除单元格高亮"""
        if self.last_highlighted:
            row = self.last_highlighted["row"]
            col = self.last_highlighted["col"]
            item = self.item(row, col)
            if item:
                item.setBackground(self.last_highlighted["bg"])
            self.last_highlighted = None
    def _process_text_data(self, text, start_row, start_col):
        """处理文本格式数据"""
        items = text.split(';')
        current_row, current_col = start_row, start_col
        
        for item_data in items:
            parts = item_data.split(',', 2)
            if len(parts) != 3:
                continue
            _, _, text = parts
            
            # 检查数字列
            if current_col in self.number_columns and not text.replace('.', '', 1).isdigit():
                text = f"[{text}]"  # 标记无效数字
            
            self._insert_item(current_row, current_col, text)
            
            # 处理列溢出
            current_col += 1
            if current_col >= self.columnCount():
                current_row += 1
                current_col = 0
                if current_row >= self.rowCount():
                    self.insertRow(current_row)

    def _process_model_data(self, mime_data, start_row, start_col):
        """处理Qt表格格式数据"""
        data = mime_data.data("application/x-qabstractitemmodeldatalist")
        stream = QDataStream(data, QDataStream.ReadOnly)
        
        row_s = stream.readInt32()
        current_row, current_col = start_row, start_col
        

        # 源行列索引(这里不需要使用)
        col_s=stream.readInt32()
        item_count=stream.readInt32()
        item_data = {}
        
        for _ in range(item_count):
            role = stream.readInt32()
            value = stream.readQVariant()
            item_data[role] = value
        
        # 提取显示文本和样式
        text = item_data.get(Qt.DisplayRole, "未知数据")
        background = item_data.get(Qt.BackgroundRole, QColor(255, 255, 255))
        alignCenter =item_data.get(Qt.TextAlignmentRole,Qt.AlignCenter)
        item = QTableWidgetItem(text)
        item.setTextAlignment(alignCenter)  
        # 插入单元格
        self.setItem(current_row, current_col, item)
        # item.setBackground(background)
    def _mime_dataToDict(self,mime_data:QMimeData)->list:
        """解析mimeData到dict

        Args:
            mime_data (QMimeData): _description_

        Returns:
            list: _description_
        """
        ListData={}
        data = mime_data.data("application/x-qabstractitemmodeldatalist")
        stream = QDataStream(data, QDataStream.ReadOnly)
        
        row_ = stream.readInt32()
        col_ = stream.readInt32()
        ListData["row"]=row_
        ListData['col']=col_
        for _ in range(stream.readInt32()):
            role = stream.readInt32()
            value = stream.readQVariant()
            ListData[role]=value
        return ListData

class TeacherList(basetable):
    s1=Signal(QDrag)
    def __init__(self, rows=100, cols=5, parent=None):
        super().__init__(rows, cols, parent)
        self.setHorizontalHeaderLabels(["姓名","倒1","倒2","总计","排序"])
        self.setColumnWidth(0,60)
        self.setColumnWidth(1,30)
        self.setColumnWidth(2,30)
        self.setColumnWidth(3,40)
        self.setSortingEnabled(True)
        self.setDragDropMode(QTableWidget.DragOnly)
        self.refreshlist()
        

    def refreshlist(self):
        (s,r)=db.runsql("select name from teacher_list where status =1;")
        if(s):
            self.clear()
            r=r.fetchall()
            if(len(r)>0):
                self.setRowCount(len(r))
                for i in range(len(r)):
                    item=QTableWidgetItem(r[i][0])
                    item.setTextAlignment(Qt.AlignCenter)  
                    self.setItem(i,0,item)
        else:
            QMessageBox.warning(self,"warning","读取教师列表失败")
    def startDrag(self, supported_actions):
        """重写开始拖动方法，自定义拖动数据和视觉反馈"""
        log.debug("teacherListStartDrag")
        
        selected_items = self.selectedItems()
        if not selected_items:
            return super().startDrag(supported_actions)
            # 执行拖动并处理结果
        
        drag=self.creatDrag(selected_items[0])
        if drag is False :
            return False
        self.s1.emit(drag)
        drag.exec_(supported_actions)


class TeacherExchange(basetable):
    def __init__(self,examRoomNum=20,examTeacherNum=2,examNum=5, parent=None):
        
        self.examRoomNum=examRoomNum
        self.examTeacherNum=examTeacherNum
        self.sessionNum=examNum
        self.self_list=set()
        self.available_list=set()
        self.activeExam=int(db.getData("activeExamID"))

        super().__init__(examRoomNum+1, examTeacherNum*examNum+1, parent)
        self.forbidden_first_row_col={(0,i*examTeacherNum+1) for i in range(examNum)}
        self.forbidden_first_row_col.update({(i,0) for i in range(examRoomNum+1)})

        for i in range(examNum):
            self.setSpan(0,i*examTeacherNum+1,1,examTeacherNum)

        self.forbidden_cells.update(self.forbidden_first_row_col)
        self.setRowHeight(0,100)
        self.horizontalHeader().setVisible(False)
        self.verticalHeader().setVisible(False)
        

        # self.bg_forbidden=QColor("#ffc107")
        self.bg_forbidden=None
        self.bg_self=QColor("#0d6efd")
        self.bg_available=QColor("#0dcaf0")
        self.bg_available_alternating=None
        self.bg_first_row=QColor("#ffffff")
        self.bg_first_col=QColor("#ffffff")
        self.bg_alternating0=QColor("#ffffff")
        self.bg_alternating1=QColor("#f3ebeb")
        # self.bg_alternating0=QColor("#F4E2A6")
        # self.bg_alternating1=QColor("#FFECAD")

        self.reasonForRejection=None
        self.loadTable()
        self.getClassConflictTeacherList()
        self.refreshBackground()

    def loadTable(self):
        dbdate=[]
        for i in range(self.sessionNum):
            for n in range(self.examRoomNum):
                for k in range(self.examTeacherNum):
                    dbdate.append((i,n,k))
                    item=self.item(n+1,i*self.examTeacherNum+k+1)
                    item.setText("")
        self.refreshBackground()

        (s,r)=db.runsql("delete from session_cache")
        if(s is False): 
            QMessageBox.critical(self,"清空表失败",str(r))
            return False
        # (s,r)=db.runmanysql(f"INSERT INSERT OR REPLACE	 INTO `session_cache` (`session`, `room`, `sort`,`name`) VALUES (?, ?, ?,'')",dbdate)
        # if(s is False): 
        #     QMessageBox.critical(self,"新建表失败",str(r))
        #     return False
        (s,r)=db.runsql(f"select address,name from session_info where examID={self.activeExam}")
        if(s is False):
            QMessageBox.critical(self,"读取监考表失败",str(r))
            return False
        data_=list()
        for i in r.fetchall():
            add=i[0]
            add=add.split(",")
            add.append(i[1])
            data_.append(add)
            item=self.item(int(add[1])+1,int(add[0])*self.examTeacherNum+int(add[2])+1)
            item.setText(add[3])
        (s,r)=db.runmanysql("INSERT OR REPLACE INTO `session_cache` (`session`, `room`, `sort`, `name`) VALUES (?, ?, ?,?)",data_)
        if not s:
            QMessageBox.critical(self,"读取监考表失败2",str(r))
            return False



    def saveTable(self):
        (s,r)=db.runsql("select session,room,sort,name from session_cache")
        data_=[]
        for i in r.fetchall():
            if i[3]=="" : continue
            add=",".join(map(str,i[0:3]))
            data_.append((self.activeExam,add,i[3]))
        db.runsql(f"delete from session_info where examID={self.activeExam}")
        db.runmanysql("INSERT INTO session_info (examID, address, name) VALUES (?, ?, ?)",data_)
        

    def availabilityCheck(self,mineDate:QMimeData,address:tuple[int]=None)->bool:
        """_summary_

        Args:
            mineDate (QMimeData): _description_
            address (tuple[int], optional): _description_. Defaults to None.

        Returns:
            bool: _description_
        """
        data = mineDate.data("application/x-qabstractitemmodeldatalist")
        stream = QDataStream(data, QDataStream.ReadOnly)
        
        row_ = stream.readInt32()
        col_=stream.readInt32()
        item_count=stream.readInt32()
        item_data = {}
        for _ in range(item_count):
            role = stream.readInt32()
            value = stream.readQVariant()
            item_data[role] = value
        print(item_data)
        return True
    @Slot()
    def getAvailability(self,drag)->set:
        """获取可安排的监考场次
        Args:
            drag (QDrag): 拖动对象
        """
        mine_Data=drag.mimeData()
        data_=self._mime_dataToDict(mine_Data)
        name=data_[0]
        temp=set()
        temp.update(self.forbidden_first_row_col)

        log.debug(name)

        
        for i in range(self.sessionNum):
            #每个场次只能监考一次
            s,r=db.runsql(f"select name from session_cache where name='{name}' and session = {i}")
            if(s is False):
                QMessageBox.critical(self,"检查时出错",str(r))
            if(len(r.fetchall())>0):
                self._add_forbidden_session(i)
                log.debug(f"第{i+1}场不可排")
            elif(i in self.conflictTeacherList) :#检查监考场次和课时冲突
                if(name in self.conflictTeacherList[i]):
                    self._add_forbidden_session(i)
                    log.debug(f"第{i+1}场有课不可排")

    def _add_forbidden_session(self,session):
        temp=set()
        for i in range(self.examTeacherNum):
            l={(n+1,session*self.examTeacherNum+i+1) for n in range(self.examRoomNum)}
            self.forbidden_cells.update(l)
    def _getSessionAddress(self,session:int)->set:
        """表格中该场次监考的地址集合

        Args:
            session (int): 监考场次的序号

        Returns:
            set: 地址集合
        """
        temp=set()
        for i in range(self.examTeacherNum):
            l={(n+1,session*self.examTeacherNum+i+1) for n in range(self.examRoomNum)}
            temp.update(l)
        return temp
    
    def getRejectionList(self,name:str)->set:
        """获取不可用地址的集合

        Args:
            name (str): 教师姓名

        Returns:
            set: 不可用的地址集合
        """
        temp=set()
        log.debug(name)
        for i in range(self.sessionNum):
            #每个场次只能监考一次
            s,r=db.runsql(f"select name from session_cache where name='{name}' and session = {i}")
            if(s is False):
                QMessageBox.critical(self,"检查时出错",str(r))
            if(len(r.fetchall())>0):
                self._add_forbidden_session(i)
                log.debug(f"第{i+1}场不可排")
            elif(i in self.conflictTeacherList) :#检查监考场次和课时冲突
                if(name in self.conflictTeacherList[i]):
                    self._add_forbidden_session(i)
                    log.debug(f"第{i+1}场有课不可排")

    def getClassConflictTeacherList(self):
        self.conflictTeacherList={}
        (s,r)=db.runsql(f"select session, confilct_week,confilct_class_time,confilct_class_name from exam_info where examID={self.activeExam}")
        if(s):
            r=r.fetchall()
        else:
            log.error(f"监考设置读取错误 {str(r)}")
        for i in r:
            if(i[1] is None):continue
            try:
                classlist=i[3]
                classlist=classlist.split(",")
                classlist="\',\'".join(classlist)
                sql=f"select `name` from `main` where `week`={i[1]} and `times` in ({i[2]}) and class in ('{classlist}')"
                log.debug(sql)
                result=db.classTimeCur.execute(sql)
            except db.Error as e:
                db.classTimeDb.rollback()
                log.error(f"读取课表失败 {str(e)}")
                QMessageBox.critical(self,"读取课表失败","str(e)")
                return False
            result=result.fetchall()
            tl=set()
            for n in result:
                tl.update(n)
            self.conflictTeacherList[int(i[0])]=tl
        print(self.conflictTeacherList)
            
    def startDrag(self, supported_actions):
        """重写开始拖动方法，自定义拖动数据和视觉反馈"""
        log.debug("teacherListstartDrag")
        selected_items = self.selectedItems()
        if not selected_items:
            return super().startDrag(supported_actions)
            # 执行拖动并处理结果
        drag=self.creatDrag(selected_items[0])
        if drag is False :
            return False
        
        self.availabilityCheck(drag.mimeData())
        self.getAvailability(selected_items[0].text())
        drag.exec_(supported_actions)
    
    # def dragMoveEvent(self, event):
    #     pos = event.pos()
    #     index = self.indexAt(pos)
        
    #     if not index.isValid():
    #         event.ignore()
    #         return
    #     row, col = index.row(), index.column()
    #     print(row,col)
    #     (s,r)=self.col2session(col)
    #     print(f"ss:{s}{r}")
    #     print(self.session2Col(s,r))
            
    #     row, col = index.row(), index.column()

    def dropEvent(self, event):
        """处理最终放下的数据"""
        # 清除高亮

        
        self._clear_highlight()

        #检查监考是否可用
        mime_data = event.mimeData()
        self.availabilityCheck(mime_data )
        
        # 获取目标位置
        pos = event.pos()
        index = self.indexAt(pos)
        target_row, target_col = 0, 0
        
        if index.isValid():
            target_row, target_col = index.row(), index.column()
        else:
            # 拖到空白处，放到最后一行
            target_row = self.rowCount()
            target_col = 0
            self.insertRow(target_row)
        
        # 解析并处理数据
        
        if mime_data.hasFormat("application/x-qabstractitemmodeldatalist"):
            self._process_model_data(mime_data, target_row, target_col)
        elif mime_data.hasText():
            self._process_text_data(mime_data.text(), target_row, target_col)

        add=self.col2session(target_col)
        db.runsql(f"INSERT OR REPLACE INTO `session_cache` (`session`, `room`, `sort`, `name`) VALUES ({add[0]},{target_row-1}, {add[1]},'{mime_data.text().split(",")[2]}')")
            
        event.acceptProposedAction()



    def get_forbidden(self,username:str,cusername:str):
        pass

    def session2Col(self,session:int,sort:int):
        return int(session*self.examTeacherNum+sort+1)

    def col2session(self,col:int):
        session=(col-1)//self.examTeacherNum
        sort=(col-1)%self.examTeacherNum
        return (session,sort)

    def refreshBackground(self):
        starttime=time.time()
        for row in range(self.rowCount()):
            item=self.item(row,0)
            item.setBackground(self.bg_first_col)
        for col in range(self.columnCount()):
            item=self.item(0,col)
            item.setBackground(self.bg_first_row)
        
        for col in range(1,self.columnCount()):
            for row in range(1,self.rowCount()):
                item=self.item(row,col)
                item.setToolTip("禁止拖放")
                if(((col-1)//self.examTeacherNum)%2==0):
                    item.setBackground(self.bg_alternating1)
                else:
                    item.setBackground(self.bg_alternating0)
        log.debug(f"着色用时:{(time.time()-starttime)*1000}毫秒")



    # def refreshBackground(self):
    #     for row in range(self.rowCount()):
    #         for col in range(self.columnCount()):
    #             item=self.item(row,col)
    #             if (row, col) in self.forbidden_cells:
    #                 if(row,col) in self.forbidden_first_row_col:
    #                     item.setBackground(QColor("#FAEBD7"))
    #                 else:
    #                     item.setBackground(QColor(255, 220, 220))
    #                 item.setToolTip("禁止拖放")
    #             else:
    #                 item.setBackground(QColor(255, 255, 255))


class DemoWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QTableWidget 重写拖放方法示例")
        self.setGeometry(100, 100, 1200, 600)
        
        layout = QVBoxLayout()
        
        info_label = QLabel("""
        拖放功能说明：<br>
        - 红色单元格：禁止拖放<br>
        - 绿色单元格：仅接受数字（非数字会标记）<br>
        - 拖动时会高亮目标位置<br>
        - 支持表格间互拖和表格内拖放
        """)
        layout.addWidget(info_label)
        
        # 创建两个表格用于测试
        table_layout = QHBoxLayout()
        btn_layout=QVBoxLayout()
        self.btn=QPushButton("保存")
        self.btn1=QPushButton("读取")
        self.table1 = basetable()
        self.table = TeacherExchange()
        self.table2 = TeacherList()
        self.table2.setMaximumWidth(260)
        self.table2.setDragDropMode(QTableWidget.DragOnly)
        btn_layout.addWidget(self.btn)
        btn_layout.addWidget(self.btn1)
        table_layout.addWidget(self.table)
        table_layout.addWidget(self.table2)
        table_layout.addLayout(btn_layout)

        self.table2.s1.connect(self.table.getAvailability)
        layout.addLayout(table_layout)
        self.setLayout(layout)
        self.btn.clicked.connect(self.table.saveTable)
        self.btn1.clicked.connect(self.table.loadTable)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = DemoWindow()
    window.show()
    sys.exit(app.exec())
    